#include <global.h>
#include "dcsplayer.h"
#include "dcsproto/proto.h"

DCSPlayer::DCSPlayer(QObject *parent) : QObject(parent)
{
    struct masterParam para{};

    mDbopt = new DCSDbOperator();
    if (DCSDbOperator::getMasterParameter(para))
    {
        mFps = para.fps;
        mMode = para.playMode;
    }
    else
    {
        mFps = 25;
        mMode = DCSPlayer::ListsCycle;
    }

    mCurFrameNo = 0;
    mCurVideoIndex = 0;
    mDuration = 0;
    mPlayProgress = 0;

    quint8 iface;
    if (!DCSDbOperator::getMasterParameterInterface(iface))
    {
        qDebug() << "db get interface error,set to default";
        iface = UDP_IFACE_EN_WAN;
    }

    if (iface == UDP_IFACE_EN_WAN)
    {
        mUdp = new UdpWorker(INTERFACE_WAN_NAME);
    }
    else
    {
        mUdp = new UdpWorker(INTERFACE_LAN_NAME);
    }
    mFrameTimer = new QTimer();
    mState = DCSPlayer::Stop;
    mFrameTimer->setInterval(1000 / mFps);
    mFrameTimer->setSingleShot(false);

    connect(mFrameTimer, &QTimer::timeout, this, &DCSPlayer::tickhandler);
}

DCSPlayer::~DCSPlayer()
{
    delete mDbopt;
    delete mUdp;
    delete mFrameTimer;
}

quint8 DCSPlayer::fps() const
{
    return mFps;
}

void DCSPlayer::setFps(const quint8 &fps)
{
    mFps = fps;
}

quint8 DCSPlayer::playProgress()
{
    mPlayProgress = mCurFrameNo * 100 / mCurVideoFrameNum;

    return mPlayProgress;
}

quint64 DCSPlayer::duration() const
{
    return mDuration;
}

quint8 DCSPlayer::curVideoIndex() const
{
    return mCurVideoIndex;
}

void DCSPlayer::setCurVideoIndex(const quint8 &curVideoIndex)
{
    mCurVideoIndex = curVideoIndex;
}

void DCSPlayer::incCurVideoIndex()
{
    if (++mCurVideoIndex >= mPlayLists.size())
    {
        mCurVideoIndex = 0;
    }
}

void DCSPlayer::decCurVideoIndex()
{
    if (--mCurVideoIndex == QUINT8_MAX)
    {
        mCurVideoIndex = mPlayLists.size() - 1;
    }
}

QList<QString> DCSPlayer::playLists() const
{
    return mPlayLists;
}

void DCSPlayer::setPlayLists(const QList<QString> &playLists)
{
    mPlayLists = playLists;
}

quint8 DCSPlayer::mode() const
{
    return mMode;
}

void DCSPlayer::setMode(const quint8 &mode)
{
    mMode = mode;
}

quint8 DCSPlayer::state() const
{
    return mState;
}

void DCSPlayer::setState(const quint8 &state)
{
    mState = state;
}

quint64 DCSPlayer::curFrameNo() const
{
    return mCurFrameNo;
}

void DCSPlayer::setCurFrameNo(const quint64 &curFrameNo)
{
    mCurFrameNo = curFrameNo;
}

void DCSPlayer::videoPrepare()
{
    mFopt = new DCSFileOperator(mPlayLists[mCurVideoIndex]);
    mFopt->fileOpen();
    mFopt->readMetaData();
    mCurVideoFrameNum = mFopt->meta()->frame_num;
    setCurFrameNo(0);

    //节目已准备完毕
    emit playVideoPrepared(mCurVideoIndex);
}

void DCSPlayer::videoCleanup()
{
    mFopt->fileClose();
    delete mFopt;
}

void DCSPlayer::playerStart()
{
    if (mPlayLists.empty())
    {
        qDebug() << "play list is empty";
        return;
    }

    videoPrepare();

    if (!mFrameTimer->isActive())
    {
        mFrameTimer->start();
    }

    mState = DCSPlayer::Play;
    emit playStateChanged(mState);
}

void DCSPlayer::playerStop()
{
    mFrameTimer->stop();
    mCurFrameNo = 0;
    mState = DCSPlayer::Stop;
    emit playStateChanged(mState);
}

void DCSPlayer::playerPause()
{
    mState = DCSPlayer::Pause;
    emit playStateChanged(mState);
}

void DCSPlayer::playerContinue()
{
    mState = DCSPlayer::Play;
    emit playStateChanged(mState);
}

void DCSPlayer::playerNext()
{
    if (mPlayLists.empty())
    {
        qDebug() << "play list is empty";
        return;
    }

    mFrameTimer->stop();
    mState = DCSPlayer::Stop;
    emit playStateChanged(mState);

    qDebug() << "play next";

    incCurVideoIndex();
    videoCleanup();
    videoPrepare();

    mState = DCSPlayer::Play;
    mFrameTimer->start();
    emit playStateChanged(mState);
}

void DCSPlayer::playerPrev()
{
    if (mPlayLists.empty())
    {
        qDebug() << "play list is empty";
        return;
    }

    mFrameTimer->stop();
    mState = DCSPlayer::Stop;
    emit playStateChanged(mState);

    qDebug() << "play prev";

    decCurVideoIndex();
    videoCleanup();
    videoPrepare();

    mState = DCSPlayer::Play;
    mFrameTimer->start();
    emit playStateChanged(mState);
}

QString DCSPlayer::getCurrentVideoName()
{
    return mPlayLists[mCurVideoIndex];
}

void DCSPlayer::tickhandler()
{
    QByteArray data;
    qint64 readSize;

    //当前节目播放完毕，根据循环模式，选择下一曲还是循环当前曲
    if (mCurFrameNo >= mFopt->meta()->frame_num)
    {
        mCurFrameNo = 0;
        if (mMode == DCSPlayer::SingleCycle)
        {
            qDebug() << "replay";
        }
        else if (mMode == DCSPlayer::ListsCycle)
        {
            qDebug() << "play next";
            incCurVideoIndex();
            //先关闭当前播放文件，再打开下一个将要播放的文件
            videoCleanup();
            videoPrepare();
        }
    }

    //读取整帧数据（各分控同一帧组成一个整帧）
    readSize = mFopt->readOneFrameByNum(data, mCurFrameNo);
//    qDebug() << "read frame size:" << readSize;

    //如果是播放状态，则当前帧自增
    if (mState == DCSPlayer::Play)
    {
        mCurFrameNo++;
    }

    //发送同步帧
    char buf[60];
    memset(buf, 0x0, 60);

    struct slave_sync_type_pdu syncPdu{};
    memset(&syncPdu, 0x0, sizeof(syncPdu));
    syncPdu.type = EN_SLAVE_SYNC_TYPE_NETWORK;
    syncPdu.roleflag = EN_SLAVE_SYNC_ROLE_SLAVE;
    aklt_proto_encode((unsigned char *) buf, TYP_SLAVE_SYNC, CMD_SLAVE_SYNC, 0x0001, (unsigned char *) &syncPdu,
                      sizeof(syncPdu), SLAVE_BROADCAST);
    mUdp->dataSend(buf, 60);

    //发送自动ID分配
    struct slave_id_set_pdu autoIdPdu{};
    memset(buf, 0x0, 60);
    memset(&autoIdPdu, 0x0, sizeof(autoIdPdu));
    autoIdPdu.type = EN_SLAVE_ID_SET_TYPE_AUTO;
    autoIdPdu.param.fist_para.id = 0x001;
    aklt_proto_encode((unsigned char *) buf, TYP_SLAVE_BROADCAST_FREQ_SET, CMD_SLAVE_ID_CONF_FREQ_SET, 0x0001,
                      (unsigned char *) &autoIdPdu, sizeof(autoIdPdu), SLAVE_BROADCAST);
    mUdp->dataSend(buf, 60);

    //TODO 亮度设置帧


    char *rawDataPos = data.data(); //原始数据指针
    quint32 copiedSector = 0;       //累计已拷贝的扇区数
    //逐分控发送
    for (int slaveid = mFopt->meta()->slave_start_id; slaveid <= mFopt->meta()->slave_num; slaveid++)
    {
        char *encodeBuf;
        auto *dummy = (struct stream_first_pkg *) rawDataPos;
        quint32 allPortDataSize = 0;


        //得到当前分控所有端口视频数据总长度
        for (quint32 i = 0; i < 8; ++i)
        {
            allPortDataSize += dummy->datalen[i];
        }
        allPortDataSize += 16;  //额外加上端口数据长度的16字节

        //计算分控此包视频数据需要多少个udp包发送
        quint32 pkgNum;
        if (allPortDataSize <= STREAM_FIRST_DATA_SIZE)
        {
            pkgNum = 1;
        }
        else
        {
            pkgNum = FLOOR_FRAME(allPortDataSize - STREAM_FIRST_DATA_SIZE) + 1;
        }

        //计算装载数据需要的buffer大小，每包协议字段+数据字段
        quint32 bufSize = pkgNum * PROTO_STREAM_FIX_SIZE + allPortDataSize;
        //保证4字节对齐
        if (bufSize % 4 != 0)
        {
            bufSize = ((bufSize - 1) / 4 + 1) * 4;
        }

        encodeBuf = new char[bufSize];
        memset(encodeBuf, 0x0, bufSize);

        //已拷贝数据大小
        quint32 copiedSize = 0;
        quint32 offset = 0;
        //编码此分控各分包数据
        for (int pkgNo = 1; pkgNo <= pkgNum; pkgNo++)
        {
            //需要拷贝数据大小
            quint32 copySize;
            /* MAC包有效载荷大小为1500，其中20字节为协议字段，故IP包的payload长度: 1500 - 20 = 1480,
             * 对于UDP包又需要8字节描述UDP协议信息（仅首包），故UDP有效载荷为IP包的payload长度 1480 - 8 = 1472,
             * 如果IP包出现分包情况，则UDP包仅首包需要UDP协议信息字段，故非首包的UDP包有效载荷为 1480
            */
            quint32 pduSize;
            struct slave_stream_data_pdu segmentPdu{};

            segmentPdu.tag = SLAVE_STREAM_DATA_FIX_TAG;
            segmentPdu.pkgnum = pkgNum;
            segmentPdu.pkgno = pkgNo;

            if (pkgNo == 1)
            {
                pduSize = STREAM_FIRST_DATA_SIZE;
            }
            else
            {
                pduSize = STREAM_FOLLOW_DATA_SIZE;
            }

            if (allPortDataSize - copiedSize < pduSize)
            {
                copySize = allPortDataSize - copiedSize;
            }
            else
            {
                copySize = pduSize;
            }

            memcpy(&(segmentPdu.streamdata), rawDataPos, copySize);
            copiedSize += copySize;
            //指针偏移
            rawDataPos += copySize;
            offset += aklt_proto_encode((unsigned char *) encodeBuf + offset,
                                        TYP_SLAVE_STREAM_DATA, CMD_SLAVE_STREAM_DATA, slaveid,
                                        (unsigned char *) &segmentPdu, copySize + 10, SLAVE_UNICAST);
        }

        //累计已拷贝的扇区数
        copiedSector += mFopt->meta()->sector_per_slave_frame[slaveid - 1];
        //偏移至指定分控数据的起始位置
        rawDataPos = data.data() + copiedSector * SECTOR_SIZE;

        mUdp->dataSend(encodeBuf, bufSize);
        delete encodeBuf;
    }
}
